package com.bitrate.dagger.log.aspect;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.MDC;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import com.bitrate.dagger.common.maker.GsonMaker;
import com.bitrate.dagger.common.util.GetClientIpUtil;
import com.bitrate.dagger.log.anno.XLog;
import com.bitrate.dagger.log.context.SpringContextHolder;
import com.bitrate.dagger.log.event.XLogDTO;
import com.bitrate.dagger.log.event.XLogEvent;
import com.google.gson.Gson;

import cn.hutool.core.collection.CollectionUtil;

/**
 * @Description 监控接口响应时间
 * @author 刘小龙
 * @date 2017年3月16日 下午2:04:12
 * @version V1.3.1
 */

@Aspect
public class XLogAscept {

	private Gson gsonBulider = GsonMaker.getInstance();

	private ThreadLocal<Long> startTime = new ThreadLocal<Long>();

	private final List<Class<?>> ignoredClass = CollectionUtil.newArrayList(ServletRequest.class, ServletResponse.class,
			MultipartFile.class);

	@Around("@annotation(log)")
	public Object around(ProceedingJoinPoint point, XLog log) throws Throwable {
		XLogDTO systemApiLog = new XLogDTO();
		String traceId = MDC.get("R-TraceId");
		systemApiLog.setTraceId(traceId);
		// 业务名称
		String apiValue = log.value();
		systemApiLog.setTitle(apiValue);
		// 请求开始时间
		startTime.set(System.currentTimeMillis());
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = attributes.getRequest();
		systemApiLog.setRequestUrl(request.getRequestURL().toString());
		systemApiLog.setHttpMethod(request.getMethod());
		systemApiLog.setRemoteIp(GetClientIpUtil.getIp(request));
		String token = request.getHeader("token");
		systemApiLog.setToken(token);
		Map<String, String> headMap = new HashMap<String, String>();
		Enumeration<String> headerNames = request.getHeaderNames();
		for (Enumeration<String> e = headerNames; e.hasMoreElements();) {
			String thisName = e.nextElement().toString();
			String thisValue = request.getHeader(thisName);
			headMap.put(thisName, thisValue);
		}
		systemApiLog.setHeaderParams(gsonBulider.toJson(headMap));
		systemApiLog.setClassMethod(point.getSignature().getDeclaringTypeName() + "." + point.getSignature().getName());
		Object[] getParams = getParams(point);
		systemApiLog.setRequestParams(gsonBulider.toJson(getParams));
		Object result = point.proceed();
		systemApiLog.setResponseTime(System.currentTimeMillis() - startTime.get());
		systemApiLog.setResponseParams(gsonBulider.toJson(result));
		SpringContextHolder.publishEvent(new XLogEvent(systemApiLog));
		return result;
	}

	@AfterThrowing(value = "@annotation(log)", throwing = "exception")
	public void afterThrow(JoinPoint point, Throwable exception, XLog log) {
		XLogDTO systemApiLog = new XLogDTO();
		String traceId = MDC.get("R-TraceId");
		systemApiLog.setTraceId(traceId);
		// 业务名称
		String workValue = log.value();
		systemApiLog.setTitle(workValue);
		// 请求开始时间
		startTime.set(System.currentTimeMillis());
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = attributes.getRequest();
		systemApiLog.setRequestUrl(request.getRequestURL().toString());
		systemApiLog.setHttpMethod(request.getMethod());
		systemApiLog.setRemoteIp(GetClientIpUtil.getIp(request));
		String token = request.getHeader("token");
		systemApiLog.setToken(token);
		Map<String, String> headMap = new HashMap<String, String>();
		Enumeration<String> headerNames = request.getHeaderNames();
		for (Enumeration<String> e = headerNames; e.hasMoreElements();) {
			String thisName = e.nextElement().toString();
			String thisValue = request.getHeader(thisName);
			headMap.put(thisName, thisValue);
		}
		// 构造请求头
		systemApiLog.setHeaderParams(gsonBulider.toJson(headMap));
		systemApiLog.setClassMethod(point.getSignature().getDeclaringTypeName() + "." + point.getSignature().getName());
		// 构造请求体
		Object[] getParams = getParams(point);
		systemApiLog.setRequestParams(gsonBulider.toJson(getParams));
		systemApiLog.setException(
				stackTraceToString(exception.getClass().getName(), exception.getMessage(), exception.getStackTrace())); // 异常信息
		systemApiLog.setResponseTime(System.currentTimeMillis() - startTime.get());
		SpringContextHolder.publishEvent(new XLogEvent(systemApiLog));
	}

	/**
	 * 获取方法参数
	 */
	private Object[] getParams(JoinPoint joinPoint) {
		Object[] args = joinPoint.getArgs();
		for (int i = 0; i < args.length; i++) {
			Object o = args[i];
			if (o instanceof ServletRequest || (o instanceof ServletResponse) || o instanceof MultipartFile) {
				args[i] = o.toString();
			}
		}
		return args;
	}

	/**
	 * 转换异常信息为字符串
	 * 
	 * @param exceptionName    异常名称
	 * @param exceptionMessage 异常信息
	 * @param elements         堆栈信息
	 */
	public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
		StringBuffer strbuff = new StringBuffer();
		for (StackTraceElement element : elements) {
			strbuff.append(element + "\n");
		}
		String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString();
		return message;
	}

}